package wbbs.service;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import wbbs.domain.Board;
import wbbs.domain.BoardCategory;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

@Singleton
public class BoardService {

    LinkedHashMap<Integer, BoardCategory> categories;
    Map<Integer, Board> boards;

    final Map<Integer, Integer> boardTopicCountMap = new HashMap<Integer, Integer>();
    final Map<Integer, Integer> boardReplyCountMap = new HashMap<Integer, Integer>();

    @Inject
    QueryRunner qr;

    void prepare() throws SQLException {
        if (categories == null) {
            List<BoardCategory> categoryList = qr.query("select * from BoardCategory order by seq", new BeanListHandler<BoardCategory>(BoardCategory.class));
            List<Board> boardList = qr.query("select * from Board order by seq", new BeanListHandler<Board>(Board.class));
            categories = new LinkedHashMap<Integer, BoardCategory>();
            for (BoardCategory category : categoryList) {
                categories.put(category.id, category);
                for (Board board : boardList) {
                    if (board.boardCategoryId == category.id) {
                        category.boards.add(board);
                        board.category = category;
                    }
                }
            }
            boards = new HashMap<Integer, Board>();
            for (Board board : boardList) {
                boards.put(board.id, board);
            }
        }
        for (Map.Entry<Integer, Board> entry : boards.entrySet()) {
            Board board = entry.getValue();
            board.topicCount = getBoardTopicCount(board.id);
            board.replyCount = getBoardReplyCount(board.replyCount);
            board.masterIds = qr.query("select masterId from BoardMaster where boardId=?", new ColumnListHandler<String>(), board.id);
        }
    }

    synchronized public List<BoardCategory> listCategories() throws SQLException {
        prepare();
        return new ArrayList<BoardCategory>(categories.values());
    }


    synchronized public Board findBoard(int id) throws SQLException {
        prepare();
        return boards.get(id);
    }

    public BoardCategory findCategory(int id) throws SQLException {
        prepare();
        return categories.get(id);
    }

    synchronized public void insertCategory(BoardCategory category) throws SQLException {
        Connection con = qr.getDataSource().getConnection();
        try {
            qr.update(con, "insert into BoardCategory(name, remark, seq) values(?, ?, ?)", category.name, category.remark, category.seq);
            category.id = qr.query(con, "call identity()", new ScalarHandler<Number>()).intValue();
        } finally {
            con.close();
        }
        categories = null;
    }

    synchronized public void updateCategory(BoardCategory category) throws SQLException {
        qr.update("update BoardCategory set name=?, remark=?, seq=? where id=?", category.name, category.remark, category.seq, category.id);
        categories = null;
    }

    synchronized public void insertBoard(Board board) throws SQLException {
        Connection con = qr.getDataSource().getConnection();
        try {
            qr.update(con, "insert into Board(name, remark, boardCategoryId, seq) values(?, ?, ?, ?)", board.name, board.remark, board.boardCategoryId, board.seq);
            board.id = qr.query(con, "call identity()", new ScalarHandler<Number>()).intValue();
        } finally {
            con.close();
        }
        categories = null;
    }

    synchronized public void updateBoard(Board board) throws SQLException {
        qr.update("update Board set name=?, remark=?, seq=? where id=?", board.name, board.remark, board.seq, board.id);
        categories = null;
    }

    public int getBoardTopicCount(int boardId) throws SQLException {
        synchronized (boardTopicCountMap) {
            Integer count = boardTopicCountMap.get(boardId);
            if (count == null) {
                count = qr.query("select count(*) from Topic where boardId=?", new ScalarHandler<Number>(), boardId).intValue();
                boardTopicCountMap.put(boardId, count);
            }
            return count;
        }
    }

    public void changeBoardTopicCount(int boardId, int dx) {
        synchronized (boardTopicCountMap) {
            Integer count = boardTopicCountMap.get(boardId);
            if (count != null) {
                boardReplyCountMap.put(boardId, count + dx);
            }
        }
    }

    public int getBoardReplyCount(int boardId) throws SQLException {
        synchronized (boardReplyCountMap) {
            Integer count = boardReplyCountMap.get(boardId);
            if (count == null) {
                count = qr.query("select count(*) from Reply join Topic on Reply.topicId=Topic.id where boardId=?", new ScalarHandler<Number>(), boardId).intValue();
                boardReplyCountMap.put(boardId, count);
            }
            return count;
        }
    }

    public void changeBoardReplyCount(int boardId, int dx) {
        synchronized (boardReplyCountMap) {
            Integer count = boardReplyCountMap.get(boardId);
            if (count != null) {
                boardReplyCountMap.put(boardId, count + dx);
            }
        }
    }

    public void modifyBoardTopicCount(int boardId, int d) {
        synchronized (boardTopicCountMap) {
            Integer count = boardTopicCountMap.get(boardId);
            if (count != null) {
                boardTopicCountMap.put(boardId, count + d);
            }
        }
    }

    public void modifyBoardReplyCount(int boardId, int d) {
        synchronized (boardReplyCountMap) {
            Integer count = boardReplyCountMap.get(boardId);
            if (count != null) {
                boardReplyCountMap.put(boardId, count + d);
            }
        }
    }
}
